#pragma once
#include "Orangepch.h"
#include "Core.h"

namespace Orange
{
    enum class EventType
    {
        None = 0,
        WindowClose,
        WindowResize,
        WindowFocus,
        WindowLostFocus,
        WindowMove,
        AppTick,
        AppUpdate,
        AppRender,
        KeyPressed,
        KeyReleased,
        KeyTyped,
        MouseButtonPressed,
        MouseButtonReleased,
        MouseMoved,
        MouseScrolled
    };

    enum EventCategory
    {
        None = 0,
        EventCategoryApplication = BIT(0), // 1
        EventCategoryInput = BIT(1),       // 4
        EventCategoryKeyboard = BIT(2),    // 8
        EventCategoryMouse = BIT(3),       // 16
        EventCategoryMouseButton = BIT(4)  // 32
    };

#ifdef ORANGE_VS
#define EVENT_CLASS_TYPE(type)                                                        \
    static EventType GetStaticEventType() { return EventType::##type; };              \
    virtual EventType GetEventType() const override { return GetStaticEventType(); }; \
    virtual const char *GetName() { return #type; };

#define EVENT_CLASS_CATEGORY(category) \
    virtual int GetEventCategory() { return category; };

#endif

    class Event
    {
    public:
        bool m_Handle = false;
        virtual EventType GetEventType() const = 0;
        virtual const char *GetName() const = 0;
        virtual int GetEventCategory() const = 0;

        bool IsInCategory(EventCategory category)
        {
            return GetEventCategory() & category;
        }

        virtual std::string ToString() const = 0;
    };

    class EventDispatch
    {
        template <typename T>
        using EventFn = std::function<bool(T &)>;

    public:
        EventDispatch(Event &event) : m_Event(event)
        {
        }

        template <typename T> // T 代表具体的 Event 类型 如 KeyPressEvent : Event;
        bool Dispatch(EventFn<T> fn)
        {
            if (m_Event.GetEventType() == T::GetStaticEventType())
            {
                // 事件类型相同 执行， 否则 不执行
                m_Event.m_Handle = fn(*(T *)&m_Event); // ????
                return true;
            }
            return false;
        }

    private:
        Event &m_Event;
    };

}